﻿using System;
using GeoAPI.Geometries;
using GeoAPI.Operation.Buffer;
using NetTopologySuite.Geometries;
using NetTopologySuite.GeometriesGraph;

namespace NetTopologySuite.Operation.Buffer
{
	public class OffsetCurveBuilder
	{
		public OffsetCurveBuilder(IPrecisionModel precisionModel, IBufferParameters bufParams)
		{
			this._precisionModel = precisionModel;
			this._bufParams = bufParams;
		}

		public IBufferParameters BufferParameters
		{
			get
			{
				return this._bufParams;
			}
		}

		public Coordinate[] GetLineCurve(Coordinate[] inputPts, double distance)
		{
			this._distance = distance;
			if (distance < 0.0 && !this._bufParams.IsSingleSided)
			{
				return null;
			}
			if (distance == 0.0)
			{
				return null;
			}
			double distance2 = System.Math.Abs(distance);
			OffsetSegmentGenerator segmentGenerator = this.GetSegmentGenerator(distance2);
			if (inputPts.Length <= 1)
			{
				this.ComputePointCurve(inputPts[0], segmentGenerator);
			}
			else if (this._bufParams.IsSingleSided)
			{
				bool isRightSide = distance < 0.0;
				this.ComputeSingleSidedBufferCurve(inputPts, isRightSide, segmentGenerator);
			}
			else
			{
				this.ComputeLineBufferCurve(inputPts, segmentGenerator);
			}
			return segmentGenerator.GetCoordinates();
		}

		public Coordinate[] GetRingCurve(Coordinate[] inputPts, Positions side, double distance)
		{
			this._distance = distance;
			if (inputPts.Length <= 2)
			{
				return this.GetLineCurve(inputPts, distance);
			}
			if (distance == 0.0)
			{
				return OffsetCurveBuilder.CopyCoordinates(inputPts);
			}
			OffsetSegmentGenerator segmentGenerator = this.GetSegmentGenerator(distance);
			this.ComputeRingBufferCurve(inputPts, side, segmentGenerator);
			return segmentGenerator.GetCoordinates();
		}

		public Coordinate[] GetOffsetCurve(Coordinate[] inputPts, double distance)
		{
			this._distance = distance;
			if (distance == 0.0)
			{
				return null;
			}
			bool flag = distance < 0.0;
			double distance2 = System.Math.Abs(distance);
			OffsetSegmentGenerator segmentGenerator = this.GetSegmentGenerator(distance2);
			if (inputPts.Length <= 1)
			{
				this.ComputePointCurve(inputPts[0], segmentGenerator);
			}
			else
			{
				this.ComputeOffsetCurve(inputPts, flag, segmentGenerator);
			}
			Coordinate[] coordinates = segmentGenerator.GetCoordinates();
			if (flag)
			{
				CoordinateArrays.Reverse(coordinates);
			}
			return coordinates;
		}

		private static Coordinate[] CopyCoordinates(Coordinate[] pts)
		{
			Coordinate[] array = new Coordinate[pts.Length];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = new Coordinate(pts[i]);
			}
			return array;
		}

		private OffsetSegmentGenerator GetSegmentGenerator(double distance)
		{
			return new OffsetSegmentGenerator(this._precisionModel, this._bufParams, distance);
		}

		private double SimplifyTolerance(double bufDistance)
		{
			return bufDistance * this._bufParams.SimplifyFactor;
		}

		private void ComputePointCurve(Coordinate pt, OffsetSegmentGenerator segGen)
		{
			EndCapStyle endCapStyle = this._bufParams.EndCapStyle;
			if (endCapStyle == EndCapStyle.Round)
			{
				segGen.CreateCircle(pt);
				return;
			}
			if (endCapStyle != EndCapStyle.Square)
			{
				return;
			}
			segGen.CreateSquare(pt);
		}

		private void ComputeLineBufferCurve(Coordinate[] inputPts, OffsetSegmentGenerator segGen)
		{
			double num = this.SimplifyTolerance(this._distance);
			Coordinate[] array = BufferInputLineSimplifier.Simplify(inputPts, num);
			int num2 = array.Length - 1;
			segGen.InitSideSegments(array[0], array[1], Positions.Left);
			for (int i = 2; i <= num2; i++)
			{
				segGen.AddNextSegment(array[i], true);
			}
			segGen.AddLastSegment();
			segGen.AddLineEndCap(array[num2 - 1], array[num2]);
			Coordinate[] array2 = BufferInputLineSimplifier.Simplify(inputPts, -num);
			int num3 = array2.Length - 1;
			segGen.InitSideSegments(array2[num3], array2[num3 - 1], Positions.Left);
			for (int j = num3 - 2; j >= 0; j--)
			{
				segGen.AddNextSegment(array2[j], true);
			}
			segGen.AddLastSegment();
			segGen.AddLineEndCap(array2[1], array2[0]);
			segGen.CloseRing();
		}

		private void ComputeSingleSidedBufferCurve(Coordinate[] inputPts, bool isRightSide, OffsetSegmentGenerator segGen)
		{
			double num = this.SimplifyTolerance(this._distance);
			if (isRightSide)
			{
				segGen.AddSegments(inputPts, true);
				Coordinate[] array = BufferInputLineSimplifier.Simplify(inputPts, -num);
				int num2 = array.Length - 1;
				segGen.InitSideSegments(array[num2], array[num2 - 1], Positions.Left);
				segGen.AddFirstSegment();
				for (int i = num2 - 2; i >= 0; i--)
				{
					segGen.AddNextSegment(array[i], true);
				}
			}
			else
			{
				segGen.AddSegments(inputPts, false);
				Coordinate[] array2 = BufferInputLineSimplifier.Simplify(inputPts, num);
				int num3 = array2.Length - 1;
				segGen.InitSideSegments(array2[0], array2[1], Positions.Left);
				segGen.AddFirstSegment();
				for (int j = 2; j <= num3; j++)
				{
					segGen.AddNextSegment(array2[j], true);
				}
			}
			segGen.AddLastSegment();
			segGen.CloseRing();
		}

		private void ComputeOffsetCurve(Coordinate[] inputPts, bool isRightSide, OffsetSegmentGenerator segGen)
		{
			double num = this.SimplifyTolerance(this._distance);
			if (isRightSide)
			{
				Coordinate[] array = BufferInputLineSimplifier.Simplify(inputPts, -num);
				int num2 = array.Length - 1;
				segGen.InitSideSegments(array[num2], array[num2 - 1], Positions.Left);
				segGen.AddFirstSegment();
				for (int i = num2 - 2; i >= 0; i--)
				{
					segGen.AddNextSegment(array[i], true);
				}
			}
			else
			{
				Coordinate[] array2 = BufferInputLineSimplifier.Simplify(inputPts, num);
				int num3 = array2.Length - 1;
				segGen.InitSideSegments(array2[0], array2[1], Positions.Left);
				segGen.AddFirstSegment();
				for (int j = 2; j <= num3; j++)
				{
					segGen.AddNextSegment(array2[j], true);
				}
			}
			segGen.AddLastSegment();
		}

		private void ComputeRingBufferCurve(Coordinate[] inputPts, Positions side, OffsetSegmentGenerator segGen)
		{
			double num = this.SimplifyTolerance(this._distance);
			if (side == Positions.Right)
			{
				num = -num;
			}
			Coordinate[] array = BufferInputLineSimplifier.Simplify(inputPts, num);
			int num2 = array.Length - 1;
			segGen.InitSideSegments(array[num2 - 1], array[0], side);
			for (int i = 1; i <= num2; i++)
			{
				bool addStartPoint = i != 1;
				segGen.AddNextSegment(array[i], addStartPoint);
			}
			segGen.CloseRing();
		}

		private double _distance;

		private readonly IPrecisionModel _precisionModel;

		private readonly IBufferParameters _bufParams;
	}
}
